package com.yypx.servlet;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Serializable;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.regex.Pattern;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.jcs.JCS;
import org.apache.jcs.access.exception.CacheException;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.yypx.common.struts.ResourceMsg;

/**
 * 整合页面布局
 */
public class JSPLayoutServlet extends HttpServlet {
	private static final Log logger = LogFactory.getLog(JSPLayoutServlet.class);
	public final static String LAYOUT = "layout";
	public final static String SCREEN_CONTENT = "screen_content";
	public final static String CACHE_KEY_LAYOUT_PAGE = "CACHE_LAYOUT_%1$s";
	private String g_template_path;
	private String g_layout_path;
	private static JCS layoutMappingCache;
	static {
		try {
			layoutMappingCache = JCS.getInstance("JSPLayoutServletCache");
		} catch (CacheException e) {
			logger.error(e);
			layoutMappingCache = null;
		}
	}

	public void clearCache() {
		if (layoutMappingCache != null)
			try {
				layoutMappingCache.clear();
			} catch (CacheException e) {
				logger.error("清缓存失败");
			}
	}

	private Object getCacheByKey(String key) {
		Object obj = null;
		if (layoutMappingCache != null) {
			obj = layoutMappingCache.get(key);
		}
		return obj;
	}

	private void setCacheByKey(String key, Object obj) {
		try {
			if (layoutMappingCache != null)
				layoutMappingCache.put(key, obj);
		} catch (CacheException e) {
			logger.error(e);
		}
	}

	@Override
	public void init() throws ServletException {
		super.init();
		g_template_path = this.getInitParameter("template_path");
		if (g_template_path.endsWith("/"))
			g_template_path = g_template_path.substring(0, g_template_path.length() - 1);
		g_layout_path = this.getInitParameter("layout_path");
		if (!g_layout_path.endsWith("/"))
			g_layout_path = g_layout_path + "/";
	}

	private boolean splitPattern(String patterns, String text) {
		CharSequence tag_has = "has{", tag_hasnot = "hasnot{";
		boolean has = !patterns.contains(tag_hasnot);
		if (has)
			return Pattern.matches(".*" + patterns.replace("has{", "").replace("hasnot{", "").replace("}", "") + ".*", text);
		else {
			return !Pattern.matches(".*" + patterns.replace("has{", "").replace("hasnot{", "").replace("}", "") + ".*", text);
		}
	}

	/**
	 * 判断是否含有特殊字符，使用指定模板
	 */
	private String getLayoutBySpecialParam(String param) {
		String resultString = "";
		if (Pattern.matches(".*t_at=home.*", param)) {
			resultString = "layoutblank.jsp";
		}
		return resultString;
	}

	@SuppressWarnings("unchecked")
	@Override
	/**
	 * 读配置文件，增加模板
	 * */
	protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
		// web根路径
		String basePath = req.getScheme() + "://" + req.getServerName() + ":" + req.getServerPort() + req.getContextPath() + "/";
		String requestURI = (String) req.getAttribute("javax.servlet.include.servlet_path");
		if (requestURI == null) {
			requestURI = (String) req.getAttribute("javax.servlet.include.path_info");
			if (requestURI == null)
				requestURI = req.getRequestURI().replace(req.getContextPath(), "");
		}
		requestURI = requestURI.substring(0, requestURI.length() - 1);
		// 页面布局对象cache
		// String jspCacheString = String.format(CACHE_KEY_LAYOUT_PAGE,
		// requestURI + (req.getQueryString() != null ? req.getQueryString() :
		// ""));
		
		String paramString = req.getParameter("layout") == null ? "" : req.getParameter("layout");
		for (Enumeration<String> e = req.getParameterNames(); e.hasMoreElements();) {
			String tmpparamnameString = e.nextElement();
			if (tmpparamnameString.equals("layout")) {
				paramString += tmpparamnameString + req.getParameter(tmpparamnameString);
			}
			// 特殊的，处理collection中的文档类型，参数值也需要放在cachestring里
			if (tmpparamnameString.equals("wordType")) {
				paramString += req.getParameter(tmpparamnameString);
			}
			// 特殊的，处理个人信息和密码设置
			if (tmpparamnameString.equals("slt")) {
				paramString += req.getParameter(tmpparamnameString);
			}
			if (tmpparamnameString.equals("flashgroup")) {
				paramString += req.getParameter(tmpparamnameString);
			}
			//特殊的，处理首页设置
			if (tmpparamnameString.equals("pagecode")) {
				paramString += req.getParameter(tmpparamnameString);
			}
			// 特殊的，Added By LWM,用于联系人，联系人角色区分不同的左菜单。这个等YW想办法处理后再去掉这块代码 begin
			if (requestURI.startsWith("/ContactRole/")) {
				if (tmpparamnameString.equals("roletype")) {
					paramString += req.getParameter(tmpparamnameString);
				}
			}
			if (requestURI.startsWith("/Contact/")) {
				if (tmpparamnameString.equals("contacttype")) {
					paramString += req.getParameter(tmpparamnameString);
				}
			}
			//特殊的，用于区分是通用调查还是DRAA调查
			if (tmpparamnameString.equals("stype")) {
				paramString += req.getParameter(tmpparamnameString);
			}
			// 特殊的，Added By LWM,用于联系人，联系人角色区分不同的左菜单。这个等YW想办法处理后再去掉这块代码 end
		}
		
		if(req.getAttribute("dslt") != null){
			paramString += req.getAttribute("dslt");
		}
		//特殊的，用于区分是通用调查还是DRAA调查
		if(req.getAttribute("stype") != null){
			paramString += req.getAttribute("stype");
		}
		
		String jspCacheString = String.format(CACHE_KEY_LAYOUT_PAGE, requestURI + ((paramString == null || paramString == "") ? "" : paramString));
		LayoutMapping layoutMapping = (LayoutMapping) getCacheByKey(jspCacheString);
		if (layoutMapping == null) {
			// 将该页面pattern符合条件的配置和pattern为空的配置取出
			List<LayoutMapping> layoutMappingList = new ArrayList<LayoutMapping>();
			/* START 读配置文件，判断是否使用layout模板、页面层级、左侧子菜单定位 */
			Document document = com.yypx.common.utils.XMLUtil.getDocument(new File(getServletContext().getRealPath("/") + "cnf/layout-mapping.xml"));
			NodeList nl = document.getElementsByTagName("jspxitem");
			for (int i = 0; i < nl.getLength(); i++) {
				layoutMapping = new LayoutMapping();
				NodeList nList = nl.item(i).getChildNodes();
				boolean isThisPage = false;
				for (int j = 0; j < nList.getLength(); j++) {
					Node node = nList.item(j);
					// 支持/ContactRole/Add* 的情况 TODO:正则式
					if (node.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE && node.getNodeName().equals("jsppage") && !node.getTextContent().trim().equals("")
							&& (requestURI.startsWith(node.getTextContent().replace("*", "")) || Pattern.matches(node.getTextContent(), requestURI))) {
						isThisPage = true;
						layoutMapping.setJsppage(node.getTextContent());
					}
					if (isThisPage && node.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE && node.getNodeName().equals("pattern")) {
						layoutMapping.setPattern(node.getTextContent());
						String queryStr = req.getQueryString();
						if(req.getAttribute("dslt") != null){
							queryStr += "&dslt="+req.getAttribute("dslt");
						}
						if(req.getAttribute("stype") != null){
							queryStr += "&stype="+req.getAttribute("stype");
						}
						if (queryStr == null || !splitPattern(node.getTextContent(), queryStr))
							isThisPage = false;
					}
					if (isThisPage && node.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE && node.getNodeName().equals("getparam")) {
						layoutMapping.setGetparam(node.getTextContent());
					}
					if (isThisPage && node.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE && node.getNodeName().equals("layout")) {
						layoutMapping.setLayout(node.getTextContent());
					}
					if (isThisPage && node.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE && node.getNodeName().equals("module")) {
						layoutMapping.setModule(node.getTextContent());
					}
					if (isThisPage && node.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE && node.getNodeName().equals("submodulefile")) {
						layoutMapping.setSubmodulefile(node.getTextContent());
					}
					if (isThisPage && node.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE && node.getNodeName().equals("secondmodule")) {
						layoutMapping.setSecondmodule(node.getTextContent());
					}
					if (isThisPage && node.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE && node.getNodeName().equals("pagetitle")) {
						// layoutMapping.setPagetitle(node.getTextContent());
						try {
							layoutMapping.setPagetitle(ResourceMsg.getInstance().getResourceMsg(
									"Layout." + (layoutMapping.getSubmodulefile() != null ? layoutMapping.getSubmodulefile() : layoutMapping.getModule()) + "." + node.getTextContent()));
						} catch (Exception e) {
							layoutMapping.setPagetitle(node.getTextContent());
							e.printStackTrace();
						}
					}
					if (isThisPage && node.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE && node.getNodeName().equals("submodule")) {
						layoutMapping.setSubmodule(node.getTextContent());
					}
					if (isThisPage && node.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE && node.getNodeName().equals("pagecode")) {
						layoutMapping.setPagecode(node.getTextContent());
					}
					if (isThisPage && node.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE && node.getNodeName().equals("enable") && node.getTextContent().equals("1")) {
						layoutMapping.setEnable(node.getTextContent());
					}
				}
				if (isThisPage) {
					// 将该页面pattern符合条件的配置和pattern为空的配置取出
					layoutMappingList.add(layoutMapping);
					// break;
				}
			}
			if (layoutMappingList.size() > 1) {
				// 是否找到合适的layout
				boolean isfoundlayout = false;
				int maxParamLength = 0;
				for (LayoutMapping mapping : layoutMappingList) {
					// 符合pattern参数的配置优先
					if (mapping.getPattern() != null && mapping.getPattern() != "") {
						layoutMapping = mapping;
						isfoundlayout = true;
						break;
					} else if (mapping.getJsppage().equals(requestURI)) {// *的情况优先级最低，在有多个映射的情况下，取含有*以外的映射配置
						layoutMapping = mapping;
						isfoundlayout = true;
					} else if (mapping.getJsppage().contains("*")) {
						if (mapping.getJsppage().length() > maxParamLength)
							maxParamLength = mapping.getJsppage().length();
					}
				}
				// 有多个*的配置的情况下，则匹配通配符更长的配置

				if (!isfoundlayout) {
					for (LayoutMapping mapping : layoutMappingList) {
						if (mapping.getJsppage().contains("*") && mapping.getJsppage().length() == maxParamLength) {
							layoutMapping = mapping;
						}
					}
				}
			} else if (layoutMappingList.size() == 1) {
				layoutMapping = layoutMappingList.get(0);
			}
			else
			{
				//added by lwm at 20120301.没找到的情况下，就按照attribute里找，方便直接设置jsp页面而不需要配置layout-mapping.xml
				BufferedResponse my_res = new BufferedResponse(res);
				getServletContext().getRequestDispatcher(requestURI).include(req, my_res);
				String layout_page = (String) req.getAttribute(LAYOUT);
				String module_str = (String)req.getAttribute("page_module");
				String pagetitle_str = (String)req.getAttribute("page_title");
				layoutMapping.setLayout(layout_page);
				layoutMapping.setEnable("1");
				layoutMapping.setModule(module_str);
				layoutMapping.setPagecode(null);
				layoutMapping.setPagetitle(pagetitle_str);
				layoutMapping.setPattern(null);
				layoutMapping.setSecondmodule(null);
				layoutMapping.setSubmodule(null);
				layoutMapping.setSubmodulefile(null);
			}
			File moduleMenu = new File(getServletContext().getRealPath("/") + "cnf/" + layoutMapping.getSubmodulefile() + "Module.xml");
			if (moduleMenu.exists()) {
				document = com.yypx.common.utils.XMLUtil.getDocument(moduleMenu);
				nl = document.getElementsByTagName("menu");
				for (int i = 0; i < nl.getLength(); i++) {
					NodeList nList = nl.item(i).getChildNodes();
					for (int j = 0; j < nList.getLength(); j++) {
						Node node = nList.item(j);
						if (node.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE && node.getNodeName().equals("submodule") && node.getTextContent().equals(layoutMapping.getSubmodule())) {
							layoutMapping.setSubmodule(String.valueOf(i));
						}
					}
				}
			}
			/* END 读配置文件，判断是否使用layout模板、页面层级、左侧子菜单定位 */
			setCacheByKey(jspCacheString, layoutMapping);
		}
		// 页面布局对象cache
		// 判断是否含有特殊字符，使用指定模板
		String specialLayout = getLayoutBySpecialParam((req.getQueryString() != null ? req.getQueryString() : ""));
		if (specialLayout != "") {
			layoutMapping.setLayout(specialLayout);
		}
		// 请求页面url
		String jsp_uri = g_template_path + requestURI;
		// 判断是否附带原页面参数 0:不附带 1:附带
		if (layoutMapping.getGetparam() != null && layoutMapping.getGetparam().equals("1")) {
			if (req.getHeader("Referer") != null && req.getHeader("Referer").split("\\?").length > 1)
				jsp_uri += "?" + req.getHeader("Referer").split("\\?")[1];
		}
		BufferedResponse my_res = new BufferedResponse(res);
		getServletContext().getRequestDispatcher(jsp_uri).include(req, my_res);
		String screenContent = my_res.getScreenContent();
		if ((layoutMapping.getLayout() != null) && (layoutMapping.getLayout() != "") && ("1".equals(layoutMapping.getEnable()))) {
			// 设置参数
			req.setAttribute("page_title", layoutMapping.getPagetitle());
			req.setAttribute("module", layoutMapping.getModule());
			req.setAttribute("submodulefile", layoutMapping.getSubmodulefile());
			// req.setAttribute("pagelevel", getpagelevel(jsp_uri));
			req.setAttribute("pagelevel", basePath);
			if (layoutMapping.getSecondmodule() != null) {
				req.setAttribute("secondmodule", layoutMapping.getSecondmodule());
			}
			if (layoutMapping.getSubmodule() != null)
				req.setAttribute("submoduleindex", Integer.parseInt(layoutMapping.getSubmodule()));
			if (layoutMapping.getPagecode() != null)
				req.setAttribute("pagecode", layoutMapping.getPagecode());
			String showleft = "1";
			Cookie[] cookies = req.getCookies();
			if (cookies != null && cookies.length > 0) {
				for (int i = 0; i < cookies.length; i++) {
					if (cookies[i].getName().equals("showleftmenu")) {
						showleft = cookies[i].getValue();
					}
				}
			}
			req.setAttribute("showleft", showleft);
			// 设置参数
			// 执行layout页面
			req.setAttribute(SCREEN_CONTENT, screenContent);
			String layout_uri = g_layout_path + layoutMapping.getLayout();
			// 合并结果
			try {
				getServletContext().getRequestDispatcher(layout_uri).include(req, res);
			} catch (Exception e) {
				logger.error(e.getMessage());
			}
		} else {
			res.getWriter().append(screenContent);
		}
	}
}

/**
 * Response封装
 */
class BufferedResponse extends HttpServletResponseWrapper {
	StringWriter sout;
	PrintWriter pout;

	public BufferedResponse(HttpServletResponse res) {
		super(res);
		sout = new StringWriter();
		pout = new PrintWriter(sout);
	}

	@Override
	public PrintWriter getWriter() throws IOException {
		return pout;
	}

	protected String getScreenContent() {
		return sout.toString();
	}
}

class LayoutMapping implements Serializable {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private String jsppage;
	private String pattern;
	private String layout;
	private String pagetitle;
	private String module;
	private String secondmodule;
	private String submodule;
	private String pagecode;
	private String enable;
	private String getparam;
	private String submodulefile;

	/**
	 * @param jsppage
	 *            the jsppage to set
	 */
	public void setJsppage(String jsppage) {
		this.jsppage = jsppage;
	}

	/**
	 * @return the jsppage
	 */
	public String getJsppage() {
		return jsppage;
	}

	/**
	 * @param layout
	 *            the layout to set
	 */
	public void setLayout(String layout) {
		this.layout = layout;
	}

	/**
	 * @return the layout
	 */
	public String getLayout() {
		return layout;
	}

	/**
	 * @param pagetitle
	 *            the pagetitle to set
	 */
	public void setPagetitle(String pagetitle) {
		this.pagetitle = pagetitle;
	}

	/**
	 * @return the pagetitle
	 */
	public String getPagetitle() {
		return pagetitle;
	}

	/**
	 * @param module
	 *            the module to set
	 */
	public void setModule(String module) {
		this.module = module;
	}

	/**
	 * @return the module
	 */
	public String getModule() {
		return module;
	}

	/**
	 * @param enable
	 *            the enable to set
	 */
	public void setEnable(String enable) {
		this.enable = enable;
	}

	/**
	 * @return the enable
	 */
	public String getEnable() {
		return enable;
	}

	/**
	 * @param submodule
	 *            the submodule to set
	 */
	public void setSubmodule(String submodule) {
		this.submodule = submodule;
	}

	/**
	 * @return the submodule
	 */
	public String getSubmodule() {
		return submodule;
	}

	/**
	 * @param pagecode
	 *            the pagecode to set
	 */
	public void setPagecode(String pagecode) {
		this.pagecode = pagecode;
	}

	/**
	 * @return the pagecode
	 */
	public String getPagecode() {
		return pagecode;
	}

	/**
	 * @param getparam
	 *            the getparam to set
	 */
	public void setGetparam(String getparam) {
		this.getparam = getparam;
	}

	/**
	 * @return the getparam
	 */
	public String getGetparam() {
		return getparam;
	}

	/**
	 * @param pattern
	 *            the pattern to set
	 */
	public void setPattern(String pattern) {
		this.pattern = pattern;
	}

	/**
	 * @return the pattern
	 */
	public String getPattern() {
		return pattern;
	}

	/**
	 * @param secondmodule
	 *            the secondmodule to set
	 */
	public void setSecondmodule(String secondmodule) {
		this.secondmodule = secondmodule;
	}

	/**
	 * @return the secondmodule
	 */
	public String getSecondmodule() {
		return secondmodule;
	}

	/**
	 * @param submodulefile
	 *            the submodulefile to set
	 */
	public void setSubmodulefile(String submodulefile) {
		this.submodulefile = submodulefile;
	}

	/**
	 * @return the submodulefile
	 */
	public String getSubmodulefile() {
		return submodulefile;
	}

}
